package com.kurancy.web.authentication.service;

import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.List;

import javax.persistence.TypedQuery;
import javax.servlet.http.HttpServletRequest;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.json.JSONException;
import org.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.authentication.dao.AbstractUserDetailsAuthenticationProvider;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.AuthenticationException;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetails;
import org.springframework.stereotype.Service;

import com.kurancy.domain.KurancyUser;
import com.kurancy.domain.UserAuth;
import com.kurancy.domain.enums.State;
import com.kurancy.web.authentication.RegistrationNotFoundException;
import com.kurancy.web.authentication.SessionManager;
import com.kurancy.web.authentication.exception.InvalidLoginException;
import com.visural.common.IOUtil;

@Service
public class AuthServiceImpl extends AbstractUserDetailsAuthenticationProvider implements AuthService {
	
	/** The logger. */
	private static Log logger = LogFactory.getLog(AuthServiceImpl.class);
	
	@Autowired
	private SessionManager sessionManager;

	@Override
	/* (non-Javadoc)
	 * @see org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider#additionalAuthenticationChecks(org.springframework.security.userdetails.UserDetails, org.springframework.security.providers.UsernamePasswordAuthenticationToken)
	 */
	protected void additionalAuthenticationChecks(UserDetails arg0, UsernamePasswordAuthenticationToken arg1) throws AuthenticationException {
	}	

	/* (non-Javadoc)
	 * @see org.springframework.security.providers.dao.AbstractUserDetailsAuthenticationProvider#retrieveUser(java.lang.String, org.springframework.security.providers.UsernamePasswordAuthenticationToken)
	 */
	public UserDetails retrieveUser(String token, UsernamePasswordAuthenticationToken usernamePasswordToken) throws AuthenticationException {
		
		logger.debug("start:");
		
		if(StringUtils.isEmpty(token) )
		{
			throw new InvalidLoginException("Invalid authentication token");
		}
		
		
			JSONObject resp;
			try {
				resp = new JSONObject(
				    IOUtil.urlToString(new URL("https://graph.facebook.com/me?access_token=" + token)));
		
				String facebookUserId = resp.getString("id");
		        String firstName = resp.getString("first_name");
		        String lastName = resp.getString("last_name");
		        String email = resp.getString("email");
		        String link = resp.getString("link");
		        String gender = resp.getString("gender");
		        String locationName = null;
		        try {
		        	JSONObject location = new JSONObject(resp.getString("location"));
		        	locationName = location.getString("name");
		        } catch (JSONException e) {
		        	logger.debug("Location not set for this FB User");
		        }
		        logger.debug(facebookUserId + firstName + lastName + email + link + gender + locationName);
			
	        
	        
		        KurancyUser registeredKurancyUser = null;
		        TypedQuery<KurancyUser> userQuery = KurancyUser.findKurancyUsersByFacebookUserIdEquals(facebookUserId);
		        
		        UserAuth userDetails = new UserAuth();
		        
		        if (userQuery != null && userQuery.getResultList().size() > 0) {
					registeredKurancyUser = userQuery.getResultList().get(0);
				
					List<GrantedAuthority> auths = new ArrayList<GrantedAuthority>();
					GrantedAuthority ga = new AuthenticatedKurancyUser();
					auths.add(ga);
					userDetails.setAuthorities(auths);
					userDetails.setKurancyUser(registeredKurancyUser);
					
					SecurityContextHolder.getContext().setAuthentication(userDetails);
					return userDetails;
		        }else {
	            	logger.debug("User was authenticated by Facebook but has not registered with Kurancy yet.  Forwarding to registration flow.");
	            	KurancyUser unregistered = new KurancyUser();
	            	unregistered.setFacebookUserId(facebookUserId);
	            	unregistered.setFirstName(firstName);
	            	unregistered.setLastName(lastName);
	            	unregistered.setEmail(email);
	            	
	            	//try to set the city and state from the fetched locations
	            	if (locationName != null) {
	            		String[] locations = locationName.split(",");
	            		if (locations[0] != null) {
	            			unregistered.setCity(locations[0].trim());
	            		}
	            		if (locations[1] != null && locations[1].length() > 0) {
	            			//attempt to get the state abrreviation from the second parameter of the location
	            			State state =  State.fromName(locations[1].trim());
	            			if (state != null) {
	            				unregistered.setStateAbbr(state.getStateAbbreviation());
	            			}
	            		}
	            	}
	            	throw new RegistrationNotFoundException(unregistered);
	            }
			} catch (JSONException e) {
				logger.error(e, e);
				throw new InvalidLoginException(e.getMessage());
			}catch (IOException e) {
				logger.error(e, e);
				throw new InvalidLoginException(e.getMessage());
			}
	}

	@Override
	public void cacheNonFacebookAuthentication(Authentication springAuth, HttpServletRequest request) {
		UserAuth auth = new UserAuth();
		auth.setAuthenticated(true);
		auth.setAuthorities((List<GrantedAuthority>) springAuth.getAuthorities());
		KurancyUser kurancyUser = new KurancyUser();
		kurancyUser.setFirstName("admin");
		kurancyUser.setLastName("user");
		auth.setKurancyUser(kurancyUser);
		
		registerUserInContext(auth);
		sessionManager.removeKurancyUser(request);
	}
	
	@Override
	public void cacheFacebookAuthentication(KurancyUser registeredKurancyUser, HttpServletRequest request) {
		UserAuth auth = new UserAuth();
		List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
		GrantedAuthority ga = new AuthenticatedKurancyUser();
		authorities.add(ga);
		auth.setAuthorities(authorities);
		auth.setKurancyUser(registeredKurancyUser);
		
		registerUserInContext(auth);
		sessionManager.removeKurancyUser(request);
	}
	
	private void registerUserInContext(UserAuth userAuth) {
		SecurityContextHolder.getContext().setAuthentication(userAuth);
	}

	@Override
	public KurancyUser getKurancyUser() {
		UserAuth userAuth = (UserAuth) SecurityContextHolder.getContext().getAuthentication();
		return userAuth.getKurancyUser();
	}

	@Override
	public void updatePaypalCredentials(String paypalCredential) {
		KurancyUser user = getKurancyUser();
		TypedQuery<KurancyUser> query = KurancyUser.findKurancyUsersByFacebookUserIdEquals(user.getFacebookUserId());
		KurancyUser updateUser = query.getSingleResult();
		updateUser.setPaypalCredential(paypalCredential);
		updateUser.persist();
		
		UserAuth userAuth = (UserAuth) SecurityContextHolder.getContext().getAuthentication();
		userAuth.setKurancyUser(updateUser);
		registerUserInContext(userAuth);
	}
}
